/**
 * \file SymbolTable.cpp
 *
 * Asteriskos Compiler 1
 * Written 2010-2011, by Anthony Berlin and Alexander Carlqvist
 *
 * This file contains all of the SymbolTable functions. A symbol table
 * is a local container for symbols (variables, functions, etc).
 * It provides functions for symbol lookups, used by the semantical phase.
 *
 * This file is part of ICC.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "SymbolTable.h"
#include "Parser.h"

///
/// Constructors for 'SymbolTable'.
///
SymbolTable::SymbolTable(SymbolTable* parent, Parser* parser)
{
    init();

    m_parent = parent;
    m_parser = parser;
}

SymbolTable::SymbolTable()
{
    init();
}

///
/// Initialize the symbol table by setting default values to its data members and allocating memory
/// for the declarations.
///
inline void SymbolTable::init()
{
    m_declarationTable = new (GC) unordered_map<string, Symbol*>;
    m_declarationList = new (GC) list<Symbol*>;
    m_stack = new (GC) list<SymbolTable*>;
    m_parent = NULL;
    m_funcSymbol = NULL;
    m_parser = NULL;
}

///
/// Push a symbol onto the stack of the symbol table.
///
void SymbolTable::push(Symbol* symbol)
{
    if (symbol == NULL || symbol->m_token == NULL_TOKEN)
        return;

    m_declarationList->push_back(symbol);
    (*m_declarationTable)[symbol->m_token->m_lexeme] = symbol;
}

///
/// Returns the named symbol from this specific symbol table. Returns NULL if no symbol with the
/// specified name was found.
///
Symbol* SymbolTable::get_symbol(string lexeme)
{
    if (this == NULL || lexeme == "")
        return NULL;

    foreach (Symbol* symbol, *m_declarationList)
        if (symbol->m_token && symbol->m_token->m_lexeme == lexeme)
            return symbol;

    return NULL;
}

///
/// Returns the named symbol from this symbol table or any symbol table global to this one. Returns
/// NULL if no symbol with the specified name was found.
///
Symbol* SymbolTable::get_symbol_in_scope(string& lexeme)
{
    if (lexeme == "")
        return NULL;

    Symbol* symbol = get_symbol(lexeme);

    if (symbol == NULL)
    {
        if (m_parent == NULL)
            return NULL;
        else
            return m_parent->get_symbol_in_scope(lexeme);
    }

    return symbol;
}

///
/// Returns the symbol table containing this one (its "parent").
///
SymbolTable* SymbolTable::get_parent() const
{
    if (this == NULL || m_parent == NULL)
        return NULL;

    return m_parent;
}

///
/// Returns the stack of local symbol tables.
///
list<SymbolTable*>* SymbolTable::get_stack() const
{
    if (m_stack == NULL)
        return NULL;

    return m_stack;
}

///
/// If an identifier has been declared, using datatypes as well, if we're talking functions, we
/// return the symbol that this identifier+datatype combination represents. This function
/// recursively searches all symbol tables that are global to this one, for a declaration.
///
Symbol* SymbolTable::is_declared(string lexeme, Symbol* comparisonSymbol, bool defunc)
{
    if (this == NULL)
        return NULL;

    Symbol* symbol = get_symbol(lexeme);

    if (symbol == NULL)
    {
        if (m_parent != NULL)
            return m_parent->is_declared(lexeme, comparisonSymbol, defunc);
        else
            return NULL;
    }

    if (comparisonSymbol == NULL)
        return NULL;

    // For variables.
    if (comparisonSymbol->m_type == SYMBOL_VAR)
        return symbol;
    // For functions.
    else if (comparisonSymbol->m_type == SYMBOL_FUNC)
    {
        string dt1 = "<unknown>";
        string dt2 = "<unknown>";

        if (comparisonSymbol->m_datatype != NULL)
            dt1 = comparisonSymbol->m_datatype->to_string(defunc, true);
        if (symbol->m_datatype != NULL)
            dt2 = symbol->m_datatype->to_string(defunc, true);

        foreach (symbol, *m_declarationList)
        {
            dt2 = symbol->m_datatype->to_string(defunc, true);
            if (dt1 == dt2)
                return symbol;
        }

        if (m_parser != NULL)
        {
            m_parser->parser_warning(
                WARNING_A_MAY_SHADOW_DECL_OF_VAR, lexeme, symbol->m_token->m_line);
        }

        return m_parent->is_declared(lexeme, comparisonSymbol, defunc);
    }

    // We definately failed.
    return NULL;
}
